<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
    <script>
    var gaProperty = 'UA-2577926-1';
    // Disable tracking if the opt-out cookie exists.
    var disableStr = 'ga-disable-' + gaProperty;
    if (document.cookie.indexOf(disableStr + '=true') > -1) {
      window[disableStr] = true;
    }
    function gaOptout() {
      document.cookie = disableStr + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
      window[disableStr] = true;
    }
    function gaOptoutRevoke() {
      document.cookie = disableStr + '=false; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
      window[disableStr] = false;
    }
    </script>
    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-2577926-1"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
      gtag('config', 'UA-2577926-1', { 'anonymize_ip': true });
    </script>
    <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.css" />
    <script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script>
    <script src="https://unpkg.com/lz-string@1.4.4/libs/lz-string.min.js"></script>
    <script>
    window.addEventListener("load", function() {
      window.cookieconsent.initialise({
        'palette': {
          'popup': {
            'background': '#eaf7f7',
            'text': '#5c7291'
          },
          'button': {
            'background': '#56cbdb',
            'text': '#ffffff'
          }
        },
        'theme': 'edgeless',
        'type': 'opt-out',
        'onInitialise': function (status) {
          if (!this.hasConsented()) {
            gaOptout()
          }
        },
        'onStatusChange': function(status, chosenBefore) {
          if (!this.hasConsented()) {
            gaOptout()
          }
        },
        'onRevokeChoice': function() {
          gaOptoutRevoke()
        }
      })
    });
    </script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" type="text/css">
    <link rel="stylesheet" href="./resources/prism/prism-1.20.0.css" type="text/css">
    <link rel="stylesheet" href="./css/ol.css" type="text/css">
    <link rel="stylesheet" href="./resources/layout.css" type="text/css">
    <script src="https://unpkg.com/elm-pep"></script>
    <script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script>
    
    <link rel="stylesheet" href="color-manipulation.css">
    <title>Color Manipulation</title>
  </head>
  <body>

    <header class="navbar navbar-expand-sm navbar-dark mb-3 py-0" role="navigation">
      <a class="navbar-brand" href="https://openlayers.org/"><img src="./resources/logo-70x70.png" alt="">&nbsp;OpenLayers</a>

      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#olmenu" aria-controls="olmenu" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <!-- menu items that get hidden below 768px width -->
      <nav class="collapse navbar-collapse" id="olmenu">
        <ul class="nav navbar-nav ml-auto">
          <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="docdropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Docs</a>
            <div class="dropdown-menu dropdown-menu-right mb-3" aria-labelledby="docdropdown">
              <a class="dropdown-item" href="../doc/">Docs</a>
              <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="../doc/quickstart.html"><i class="fa fa-check fa-fw mr-2 fa-lg"></i>Quick Start</a>
                <a class="dropdown-item" href="../doc/faq.html"><i class="fa fa-question fa-fw mr-2 fa-lg"></i>FAQ</a>
                <a class="dropdown-item" href="../doc/tutorials/"><i class="fa fa-book fa-fw mr-2 fa-lg"></i>Tutorials</a>
                <a class="dropdown-item" href="/workshop/"><i class="fa fa-graduation-cap fa-fw mr-2 fa-lg"></i>Workshop</a>
                <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="https://stackoverflow.com/questions/tagged/openlayers"><i class="fa fa-stack-overflow fa-fw mr-2"></i>Ask a Question</a>
            </div>
          </li>
          <li class="nav-item active"><a class="nav-link" href="../examples/">Examples</a></li>
          <li class="nav-item"><a class="nav-link" href="../apidoc/"><i class="fa fa-sitemap mr-1"></i>API</a></li>
          <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="codedropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Code</a>
            <div class="dropdown-menu dropdown-menu-right mb-3" aria-labelledby="codedropdown">
              <a class="dropdown-item" href="https://github.com/openlayers/openlayers"><i class="fa fa-github fa-fw mr-2 fa-lg"></i>Repository</a>
              <a class="dropdown-item" href="/download/"><i class="fa fa-download fa-fw mr-2 fa-lg"></i>Download</a>
            </div>
           </li>
        </ul>
      </nav>
    </header>

    <div class="container-fluid line-numbers">

      <div id="latest-check" class="alert alert-warning alert-dismissible" role="alert" style="display:none">
        <button id="latest-dismiss" type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        This example uses OpenLayers v<span>6.5.0</span>. The <a id="latest-link" href="#" class="alert-link">latest</a> is v<span id="latest-version"></span>.
      </div>


      <div class="row-fluid">
        <a href="#" id="codepen-button" class="btn btn-link float-right">
          <i class="fa fa-codepen fa-lg"></i> Edit
        </a>
        <div class="span12">
          <h4 id="title">Color Manipulation</h4>
          <p class="tags">
            <span class="badge-group">
              <a
                href="./index.html?q=color" class="badge badge-info">color</a
              ><a
                class="badge badge-info tag-modal-toggle text-white"
                data-toggle="modal"
                data-target="#tag-example-list"
                data-title="color"
                data-content="
                  &lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./color-manipulation.html&quot;&gt;Color Manipulation&lt;/a&gt;"
                tabindex="0"
                >1</a>
            </span>
            <span class="badge-group">
              <a
                href="./index.html?q=hue" class="badge badge-info">hue</a
              ><a
                class="badge badge-info tag-modal-toggle text-white"
                data-toggle="modal"
                data-target="#tag-example-list"
                data-title="hue"
                data-content="
                  &lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./color-manipulation.html&quot;&gt;Color Manipulation&lt;/a&gt;"
                tabindex="0"
                >1</a>
            </span>
            <span class="badge-group">
              <a
                href="./index.html?q=lightness" class="badge badge-info">lightness</a
              ><a
                class="badge badge-info tag-modal-toggle text-white"
                data-toggle="modal"
                data-target="#tag-example-list"
                data-title="lightness"
                data-content="
                  &lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./color-manipulation.html&quot;&gt;Color Manipulation&lt;/a&gt;"
                tabindex="0"
                >1</a>
            </span>
            <span class="badge-group">
              <a
                href="./index.html?q=chroma" class="badge badge-info">chroma</a
              ><a
                class="badge badge-info tag-modal-toggle text-white"
                data-toggle="modal"
                data-target="#tag-example-list"
                data-title="chroma"
                data-content="
                  &lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./color-manipulation.html&quot;&gt;Color Manipulation&lt;/a&gt;"
                tabindex="0"
                >1</a>
            </span>
          </p>
          <div class="modal modal-tag-example" id="tag-example-list" tabindex="-1" role="dialog" aria-labelledby="tag-example-title" aria-hidden="true">
            <div class="modal-dialog modal-dialog-scrollable" role="document">
              <div class="modal-content">
                <div class="modal-header">
                  <h5 class="modal-title" id="tag-example-title"></h5>
                  <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                  </button>
                </div>
                <div class="modal-body">
                  <div class="list-group"></div>
                </div>
              </div>
            </div>
          </div>
          <div id="map" class="map"></div>
<table class="controls">
  <tr>
    <td><label for="hue">hue</label></td>
    <td><input id="hue" type="range" min="-180" max="180" value="0"/></td>
    <td><span id="hueOut"></span> °&nbsp;</td>
  </tr>
  <tr>
    <td><label for="chroma">chroma</label></td>
    <td><input id="chroma" type="range" min="0" max="100" value="100"/></td>
    <td><span id="chromaOut"></span> %</td>
  </tr>
  <tr>
    <td><label for="lightness">lightness</label></td>
    <td><input id="lightness" type="range" min="0" max="100" value="100"/></td>
    <td><span id="lightnessOut"></span> %</td>
  </tr>
</table>

        </div>
        <form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define">
          <input id="codesandbox-params" type="hidden" name="parameters">
        </form>
      </div>

      <div class="row-fluid">
        <div class="span12">
          <p id="shortdesc">Demonstrates color manipulation with a raster source.</p>
          <div id="docs"><p>A raster source allows arbitrary manipulation of pixel values.  In this example, RGB values on the input tile source are adjusted in a pixel-wise operation before being rendered with a second raster source.  The raster operation takes pixels in in RGB space, converts them to HCL color space, adjusts the values based on the controls above, and then converts them back to RGB space for rendering.</p>
</div>
        </div>
      </div>

      <div class="row-fluid">
        <h5 class="source-heading">main.js</h5>
        <pre><code id="example-js-source" class="language-js">import 'ol/ol.css';
import ImageLayer from &#x27;ol/layer/Image&#x27;;
import Map from &#x27;ol/Map&#x27;;
import View from &#x27;ol/View&#x27;;
import {Raster as RasterSource, Stamen} from &#x27;ol/source&#x27;;

/**
 * Color manipulation functions below are adapted from
 * https://github.com/d3/d3-color.
 */
var Xn &#x3D; 0.95047;
var Yn &#x3D; 1;
var Zn &#x3D; 1.08883;
var t0 &#x3D; 4 / 29;
var t1 &#x3D; 6 / 29;
var t2 &#x3D; 3 * t1 * t1;
var t3 &#x3D; t1 * t1 * t1;
var twoPi &#x3D; 2 * Math.PI;

/**
 * Convert an RGB pixel into an HCL pixel.
 * @param {Array&lt;number&gt;} pixel A pixel in RGB space.
 * @return {Array&lt;number&gt;} A pixel in HCL space.
 */
function rgb2hcl(pixel) {
  var red &#x3D; rgb2xyz(pixel[0]);
  var green &#x3D; rgb2xyz(pixel[1]);
  var blue &#x3D; rgb2xyz(pixel[2]);

  var x &#x3D; xyz2lab(
    (0.4124564 * red + 0.3575761 * green + 0.1804375 * blue) / Xn
  );
  var y &#x3D; xyz2lab(
    (0.2126729 * red + 0.7151522 * green + 0.072175 * blue) / Yn
  );
  var z &#x3D; xyz2lab(
    (0.0193339 * red + 0.119192 * green + 0.9503041 * blue) / Zn
  );

  var l &#x3D; 116 * y - 16;
  var a &#x3D; 500 * (x - y);
  var b &#x3D; 200 * (y - z);

  var c &#x3D; Math.sqrt(a * a + b * b);
  var h &#x3D; Math.atan2(b, a);
  if (h &lt; 0) {
    h +&#x3D; twoPi;
  }

  pixel[0] &#x3D; h;
  pixel[1] &#x3D; c;
  pixel[2] &#x3D; l;

  return pixel;
}

/**
 * Convert an HCL pixel into an RGB pixel.
 * @param {Array&lt;number&gt;} pixel A pixel in HCL space.
 * @return {Array&lt;number&gt;} A pixel in RGB space.
 */
function hcl2rgb(pixel) {
  var h &#x3D; pixel[0];
  var c &#x3D; pixel[1];
  var l &#x3D; pixel[2];

  var a &#x3D; Math.cos(h) * c;
  var b &#x3D; Math.sin(h) * c;

  var y &#x3D; (l + 16) / 116;
  var x &#x3D; isNaN(a) ? y : y + a / 500;
  var z &#x3D; isNaN(b) ? y : y - b / 200;

  y &#x3D; Yn * lab2xyz(y);
  x &#x3D; Xn * lab2xyz(x);
  z &#x3D; Zn * lab2xyz(z);

  pixel[0] &#x3D; xyz2rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z);
  pixel[1] &#x3D; xyz2rgb(-0.969266 * x + 1.8760108 * y + 0.041556 * z);
  pixel[2] &#x3D; xyz2rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);

  return pixel;
}

function xyz2lab(t) {
  return t &gt; t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
}

function lab2xyz(t) {
  return t &gt; t1 ? t * t * t : t2 * (t - t0);
}

function rgb2xyz(x) {
  return (x /&#x3D; 255) &lt;&#x3D; 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}

function xyz2rgb(x) {
  return (
    255 * (x &lt;&#x3D; 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055)
  );
}

var raster &#x3D; new RasterSource({
  sources: [
    new Stamen({
      layer: &#x27;watercolor&#x27;,
    }) ],
  operation: function (pixels, data) {
    var hcl &#x3D; rgb2hcl(pixels[0]);

    var h &#x3D; hcl[0] + (Math.PI * data.hue) / 180;
    if (h &lt; 0) {
      h +&#x3D; twoPi;
    } else if (h &gt; twoPi) {
      h -&#x3D; twoPi;
    }
    hcl[0] &#x3D; h;

    hcl[1] *&#x3D; data.chroma / 100;
    hcl[2] *&#x3D; data.lightness / 100;

    return hcl2rgb(hcl);
  },
  lib: {
    rgb2hcl: rgb2hcl,
    hcl2rgb: hcl2rgb,
    rgb2xyz: rgb2xyz,
    lab2xyz: lab2xyz,
    xyz2lab: xyz2lab,
    xyz2rgb: xyz2rgb,
    Xn: Xn,
    Yn: Yn,
    Zn: Zn,
    t0: t0,
    t1: t1,
    t2: t2,
    t3: t3,
    twoPi: twoPi,
  },
});

var controls &#x3D; {};

raster.on(&#x27;beforeoperations&#x27;, function (event) {
  var data &#x3D; event.data;
  for (var id in controls) {
    data[id] &#x3D; Number(controls[id].value);
  }
});

var map &#x3D; new Map({
  layers: [
    new ImageLayer({
      source: raster,
    }) ],
  target: &#x27;map&#x27;,
  view: new View({
    center: [0, 2500000],
    zoom: 2,
    maxZoom: 18,
  }),
});

var controlIds &#x3D; [&#x27;hue&#x27;, &#x27;chroma&#x27;, &#x27;lightness&#x27;];
controlIds.forEach(function (id) {
  var control &#x3D; document.getElementById(id);
  var output &#x3D; document.getElementById(id + &#x27;Out&#x27;);
  control.addEventListener(&#x27;input&#x27;, function () {
    output.innerText &#x3D; control.value;
    raster.changed();
  });
  output.innerText &#x3D; control.value;
  controls[id] &#x3D; control;
});
</code></pre>
      </div>

      <div class="row-fluid">
        <h5 class="source-heading">index.html</h5>
        <pre><code id="example-html-source" class="language-markup">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Color Manipulation&lt;/title&gt;
    &lt;!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --&gt;
    &lt;script src="https://unpkg.com/elm-pep"&gt;&lt;/script&gt;
    &lt;style&gt;
      .map {
        width: 100%;
        height:400px;
      }
      table.controls td {
        padding: 2px 5px;
      }
      table.controls td:nth-child(3) {
        text-align: right;
        min-width: 4.5em;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id&#x3D;&quot;map&quot; class&#x3D;&quot;map&quot;&gt;&lt;/div&gt;
    &lt;table class&#x3D;&quot;controls&quot;&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;label for&#x3D;&quot;hue&quot;&gt;hue&lt;/label&gt;&lt;/td&gt;
        &lt;td&gt;&lt;input id&#x3D;&quot;hue&quot; type&#x3D;&quot;range&quot; min&#x3D;&quot;-180&quot; max&#x3D;&quot;180&quot; value&#x3D;&quot;0&quot;/&gt;&lt;/td&gt;
        &lt;td&gt;&lt;span id&#x3D;&quot;hueOut&quot;&gt;&lt;/span&gt; °&amp;nbsp;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;label for&#x3D;&quot;chroma&quot;&gt;chroma&lt;/label&gt;&lt;/td&gt;
        &lt;td&gt;&lt;input id&#x3D;&quot;chroma&quot; type&#x3D;&quot;range&quot; min&#x3D;&quot;0&quot; max&#x3D;&quot;100&quot; value&#x3D;&quot;100&quot;/&gt;&lt;/td&gt;
        &lt;td&gt;&lt;span id&#x3D;&quot;chromaOut&quot;&gt;&lt;/span&gt; %&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;label for&#x3D;&quot;lightness&quot;&gt;lightness&lt;/label&gt;&lt;/td&gt;
        &lt;td&gt;&lt;input id&#x3D;&quot;lightness&quot; type&#x3D;&quot;range&quot; min&#x3D;&quot;0&quot; max&#x3D;&quot;100&quot; value&#x3D;&quot;100&quot;/&gt;&lt;/td&gt;
        &lt;td&gt;&lt;span id&#x3D;&quot;lightnessOut&quot;&gt;&lt;/span&gt; %&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/table&gt;
    &lt;script src="main.js"&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
      </div>


      <div class="row-fluid">
        <h5 class="source-heading">package.json</h5>
        <pre><code id="example-pkg-source" class="language-json">{
  &quot;name&quot;: &quot;color-manipulation&quot;,
  &quot;dependencies&quot;: {
    &quot;ol&quot;: &quot;6.5.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;parcel&quot;: &quot;^2.0.0-beta.1&quot;
  },
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;parcel index.html&quot;,
    &quot;build&quot;: &quot;parcel build --public-url . index.html&quot;
  }
}</code></pre>
      </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
    <script src="./resources/prism/prism-1.20.0.min.js"></script>
    <script src="./resources/common.js"></script>
    <script src="common.js"></script>
        <script src="color-manipulation.js"></script>
    <script>
      $('#tag-example-list').on('show.bs.modal', function (event) {
        const button = $(event.relatedTarget); // Button that triggered the modal
        const title = button.data('title');
        const content = button.data('content');
        const modal = $(this)
        modal.find('.modal-title').text(title);
        modal.find('.modal-body').html(content);
      });

      var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
      fetch(packageUrl).then(function(response) {
        return response.json();
      }).then(function(json) {
        var latestVersion = json.version;
        document.getElementById('latest-version').innerHTML = latestVersion;
        var url = window.location.href;
        var branchSearch = url.match(/\/([^\/]*)\/examples\//);
        var cookieText = 'dismissed=-' + latestVersion + '-';
        var dismissed = document.cookie.indexOf(cookieText) != -1;
        if (branchSearch && !dismissed && /^v[0-9\.]*$/.test(branchSearch[1]) && '6.5.0' != latestVersion) {
          var link = url.replace(branchSearch[0], '/latest/examples/');
          fetch(link, {method: 'head'}).then(function(response) {
            var a = document.getElementById('latest-link');
            a.href = response.status == 200 ? link : '../../latest/examples/';
          });
          var latestCheck = document.getElementById('latest-check');
          latestCheck.style.display = '';
          document.getElementById('latest-dismiss').onclick = function() {
            latestCheck.style.display = 'none';
            document.cookie = cookieText;
          }
        }
      });
    </script>
  </body>
</html>
